#include <QGraphicsLinearLayout>
#include <QGraphicsProxyWidget>
#include <QLabel>

#include "wpcommentgraphicslistitem.h"
#include "wpcommentgraphicslist.h"
#include "WPData.h"
#include "PopupMenu.h"
#include "themedatastorage.h"
#include "BlogView.h"
#include "wpcommongeometry.h"
#include "wpconfirmdialog.h"

#include "ShadowGraphicsItem.h"
#include "ListTitle.h"

#include "ScopedTimer.h"

WPCommentGraphicsList::WPCommentGraphicsList(
        BlogView* aBlogView, const QGraphicsView& aGraphicsView,
        PannableView* aPannableView
        ) : WPGraphicsList(aBlogView, aGraphicsView, aPannableView),
        iBlogView(aBlogView),
        iGravatars(aBlogView->GetGravatars())
{
    TIME_FUNCTION_ALWAYS

            // TODO find a proper way to align menu icon and text side by side.
            iActionApprove = new QAction(iThemeData.GetContextIcon(ThemeData::EApprove),tr("Approve"), this);
    iActionUnapprove = new QAction(iThemeData.GetContextIcon(ThemeData::EApprove),tr("Unapprove"), this);
    iActionView = new QAction(iThemeData.GetContextIcon(ThemeData::EView),tr("View"), this);
    iActionHide = new QAction(iThemeData.GetContextIcon(ThemeData::EView),tr("Minimize"), this);
    iActionReply = new QAction(iThemeData.GetContextIcon(ThemeData::EReply),tr("Reply"), this);
    iActionDelete = new QAction(iThemeData.GetContextIcon(ThemeData::ERemove),tr("Delete"), this);
    iActionSpam = new QAction(iThemeData.GetContextIcon(ThemeData::ESpam),tr("Spam"), this);

    connect(iActionApprove,   SIGNAL(triggered()), this, SLOT(Approve()));
    connect(iActionUnapprove, SIGNAL(triggered()), this, SLOT(Unapprove()));
    connect(iActionView,      SIGNAL(triggered()), this, SLOT(View()));
    connect(iActionHide,      SIGNAL(triggered()), this, SLOT(View()));
    connect(iActionReply,     SIGNAL(triggered()), this, SLOT(Reply()));
    connect(iActionDelete,    SIGNAL(triggered()), this, SLOT(Delete()));
    connect(iActionSpam,      SIGNAL(triggered()), this, SLOT(Spam()));

    connect(
            iGravatars, SIGNAL(GravatarUpdated (const QString &, const QPixmap &)),
            this,         SLOT(GravatarUpdated (const QString &, const QPixmap &))
            );
    
    connect(this,      SIGNAL(ReplyToComment(WPComment)),
            aBlogView, SLOT(ReplyToComment(WPComment)));
    
    iUnapprovedLabel = new ListTitle(this, iThemeData);
    iUnapprovedTopShadow = new ShadowGraphicsItem(ShadowGraphicsItem::Top, this);
    iUnapprovedBottomShadow = new ShadowGraphicsItem(ShadowGraphicsItem::Bottom, this);
    
    iApprovedLabel = new ListTitle(this, iThemeData);
    iApprovedTopShadow = new ShadowGraphicsItem(ShadowGraphicsItem::Top, this);
    iApprovedBottomShadow = new ShadowGraphicsItem(ShadowGraphicsItem::Bottom, this);
    
    setContentsMargins(0, 0, 0, 0);

    iLayout->addItem(iUnapprovedLabel);
    iLayout->addItem(iUnapprovedTopShadow);
    iUnapprovedLayout = AddLayout();
    iLayout->addItem(iUnapprovedBottomShadow);

    iLayout->addItem(iApprovedLabel);
    iLayout->addItem(iApprovedTopShadow);
    iApprovedLayout = AddLayout();
    iLayout->addItem(iApprovedBottomShadow);
    
    UpdateLabels();
}

WPCommentGraphicsList::~WPCommentGraphicsList()
{

}

void WPCommentGraphicsList::AddComment(WPComment aComment, bool aShowFullComment)
{
    WPCommentGraphicsListItem* item = new WPCommentGraphicsListItem(aComment, aShowFullComment, iBusyIndicator, iThemeData);
    connect(item, SIGNAL(Selected(WPGraphicsListItem*)), this, SLOT(ShowContextMenu(WPGraphicsListItem*)));

    // get gravatar for item
    item->SetAvatarImage(iGravatars->GetGravatar(aComment));
    
    if(aComment->IsUnapproved())
    {
        OrderedInsert(*iUnapprovedLayout, *item);
    }
    else
    {
        OrderedInsert(*iApprovedLayout, *item);
    }
    
    UpdateLabels();
}

void WPCommentGraphicsList::RemoveComment (WPComment aComment)
{
    WPCommentGraphicsListItem * item = 0;
    
    item = RemoveFromLayout(*iUnapprovedLayout, aComment);
    if(!item) { item = RemoveFromLayout(*iApprovedLayout, aComment); }
    
    delete item;
    UpdateLabels();
}

void WPCommentGraphicsList::GravatarUpdated (const QString &email, const QPixmap &pixmap)
{
    UpdateGravatarsInLayout(*iUnapprovedLayout, email, pixmap);
    UpdateGravatarsInLayout(*iApprovedLayout, email, pixmap);
}

void WPCommentGraphicsList::PopulatePopupMenuForCurrentItem(PopupMenu* aPopupMenu)
{
    if(!CurrentItem()) { return; }
    
    if (CurrentItem()->CanBeExpanded()) {
        if (CurrentItem()->ShowFullComment())
        {
            aPopupMenu->addAction(iActionHide);
            aPopupMenu->setActiveAction(iActionHide);
        }
        else
        {
            aPopupMenu->addAction(iActionView);
            aPopupMenu->setActiveAction(iActionView);
        }
    }
    
    if (CurrentItem()->GetComment()->IsUnapproved())
        aPopupMenu->addAction(iActionApprove);
    else
        aPopupMenu->addAction(iActionUnapprove);

    // XXX: if (iNetworkEngine.GetPost(aItem->GetComment().iPostId))
    aPopupMenu->addAction(iActionReply);

    aPopupMenu->addAction(iActionDelete);
    if (CurrentItem()->GetComment()->iStatus != QLatin1String("spam"))
        aPopupMenu->addAction(iActionSpam);
}

void WPCommentGraphicsList::Approve()
{
    if(CurrentItem())
        CurrentItem()->GetComment()->UpdateStatus("approve");
}

void WPCommentGraphicsList::Unapprove()
{
    if(CurrentItem())
        CurrentItem()->GetComment()->UpdateStatus("hold");
}

void WPCommentGraphicsList::View()
{
    if(CurrentItem()) {
        CurrentItem()->ChangeViewMode();
        // queue repositioning after context menu is hidden
        iRepositionAfterMenuHide = true;
    }
}

void WPCommentGraphicsList::Reply()
{
    if(CurrentItem())
        emit ReplyToComment(CurrentItem()->GetComment());
}

void WPCommentGraphicsList::Delete()
{
    WP_CONFIRM_AND_RETURN_ON_FALSE(iBlogView);

    if(CurrentItem())
        CurrentItem()->GetComment()->Delete();
}

void WPCommentGraphicsList::Spam()
{
    if(CurrentItem())
        CurrentItem()->GetComment()->UpdateStatus("spam");
}

void WPCommentGraphicsList::UpdateCommentsData(const WPDataBlog& aBlog)
{
    if(iCurrentBlog) {
        disconnect(iCurrentBlog, NULL, this, NULL);
    }
    iCurrentBlog = const_cast<WPDataBlog *>(&aBlog);
    
    ClearLayout(*iUnapprovedLayout);
    ClearLayout(*iApprovedLayout);
    
    // add comments, inserting labels as needed
    QListIterator<WPComment> data(aBlog.GetComments());
    while (data.hasNext()) {
        WPComment comment = data.next();

        // sorted insert, with gravatar
        AddComment(comment, false);
    }
    
    connect(
            &aBlog,  SIGNAL(CommentAdded(WPComment)),
            this,   SLOT(CommentAdded(WPComment))
            );
    connect(
            &aBlog,  SIGNAL(CommentChanged(WPComment)),
            this,   SLOT(CommentChanged(WPComment))
            );
    connect(
            &aBlog,  SIGNAL(CommentRemoved(WPComment)),
            this,   SLOT(CommentRemoved(WPComment))
            );
    
    UpdateLabels();
    
    /*
     * Calling setFocus here causes Qt to deadlock when loading initial
     * blog data iff the comment list is visible when the comment network
     * request is handled.
     * The deadlock/infinite loop happens only after returning from the
     * network handling function, not within this function.
     * 
     * This has only been encountered on the E71 :/
    setFocus();
    */
}

void WPCommentGraphicsList::CommentChanged(WPComment aComment)
{
    qDebug("WPCommentGraphicsList::CommentChanged");

    // cycle
    RemoveComment(aComment);
    AddComment(aComment, false);
    
    UpdateLabels();
}

void WPCommentGraphicsList::CommentRemoved(WPComment aComment)
{
    qDebug("WPCommentGraphicsList::CommentRemoved");

    RemoveComment(aComment);

    UpdateLabels();
}

void WPCommentGraphicsList::CommentAdded(WPComment aComment)
{
    qDebug("WPCommentGraphicsList::CommentAdded");

    AddComment(aComment, false);

    UpdateLabels();
}

void WPCommentGraphicsList::UpdateLabels()
{
    int unapproved = iUnapprovedLayout->count();
    SubLayout unapprovedLayout(iUnapprovedLabel, iUnapprovedTopShadow, iUnapprovedLayout, iUnapprovedBottomShadow);
    if (unapproved) {
        iUnapprovedLabel->setText(tr("Unapproved") + " (" + QString::number(unapproved) + ')');
        ShowSubLayout(unapprovedLayout, 0); // Show in beginning
    } else {
        HideSubLayout(unapprovedLayout);
    }

    int approved = iApprovedLayout->count();
    SubLayout approvedLayout(iApprovedLabel, iApprovedTopShadow, iApprovedLayout, iApprovedBottomShadow);
    if (approved) {
        iApprovedLabel->setText(tr("Comments") + " (" + QString::number(approved) + ')');
        ShowSubLayout(approvedLayout, iUnapprovedBottomShadow);
    } else {
        HideSubLayout(approvedLayout);
    }
}

void WPCommentGraphicsList::OrderedInsert(QGraphicsLinearLayout & layout, WPCommentGraphicsListItem & item)
{
    int i;
    for(i = 0; i < layout.count(); ++i) {
        WPCommentGraphicsListItem * cListItem = LayoutToListItem(layout.itemAt(i));
        if(*cListItem < item) { break; }
    }
    layout.insertItem(i, &item);
}

WPCommentGraphicsListItem * WPCommentGraphicsList::RemoveFromLayout(QGraphicsLinearLayout & layout, WPComment comment)
{
    for(int i = 0; i < layout.count(); ++i) {
        WPCommentGraphicsListItem * item = LayoutToListItem(layout.itemAt(i));
        if(item->GetComment() == comment) {
            IteratorGuard itGuard(*this, item);
            layout.removeAt(i);
            return item;
        }
    }
    return 0;
}

void WPCommentGraphicsList::UpdateGravatarsInLayout(QGraphicsLinearLayout & layout, const QString &email, const QPixmap &pixmap)
{
    // look for ALL items with matching email and set their pixmap
    for (int i = 0; i < layout.count(); i++) {
        WPCommentGraphicsListItem *item = LayoutToListItem(layout.itemAt(i));

        // normalize for comparison
        QString normalizedEmail = WPDataGravatars::NormalizeEmail(item->GetComment()->iAuthorEmail);

        if (normalizedEmail == email)
            // update item's pixmap
            item->SetAvatarImage(pixmap);
    }
}
